/*
Copyright (C) 2008  Helmut Juskewycz
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.juskewycz.dividere.action.join;

import com.juskewycz.dividere.action.ProgressStatus;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Implements <code>IJoiner</code> and uses one thread for joining.
 * 
 * @author Helmut Juskewycz
 * @createdon 27. January 2008
 * @lastmodified 27. January 2008
 * @history
 * 27. January 2008 Finished version 1.0
 */
public class OneThreadBufferJoiner extends AJoiner {

    private Logger logger = Logger.getLogger(OneThreadBufferJoiner.class.getName());

    @Override
    public void join(String firstPartPath) {

	if (firstPartPath == null) {
	    notifyError(new ProgressStatus(0, 0, ERROR_FIRST_PART_PATH_NOT_EXISTS, true));
	    logger.log(Level.SEVERE, ERROR_FIRST_PART_PATH_NOT_EXISTS);
	    return;
	}

	OutputStream out = null;
	try {
	    File firstPart = new File(firstPartPath);

	    if (!firstPart.exists()) {
		notifyError(new ProgressStatus(0, 0, ERROR_FIRST_FILE_NOT_EXIST, true));
		logger.log(Level.SEVERE, ERROR_FIRST_FILE_NOT_EXIST);
		return;
	    }

	    final String joinedFilePath = getOriginalFilePath(firstPart.getName());
	    if (joinedFilePath != null) {
		final String piecesFilePathStart = joinedFilePath + ".";
		File joinedFile = new File(firstPart.getParentFile(), joinedFilePath);
		File pieceDirectory = firstPart.getParentFile();
		File[] pieces = pieceDirectory.listFiles(new FilenameFilter() {

		    public boolean accept(File dir, String name) {
			return name.startsWith(piecesFilePathStart);
		    }
		});

		//sort files with the ending 001, 002, 003, ...
		Arrays.sort(pieces, fileComparator);

		if (!checkAllPartFilesAvailable(pieces)) {
		    notifyError(new ProgressStatus(0, 0, ERROR_NOT_ALL_FILES_EXISTS, true));
		    return;
		}

		notifyFileJoinBegin(joinedFile);
		if (!checkOutputFile(joinedFile)) {
		    return;
		}
		out = new FileOutputStream(joinedFile);
		notifyProgressListeners(new ProgressStatus(0, pieces.length, ""));

		for (int i = 1; i <= pieces.length; i++) {
		    File nextFile = pieces[i - 1];
		    InputStream in = null;
		    notifyPartProcessBegin(nextFile);
		    try {
			in = new FileInputStream(nextFile);
			while (true) {
			    synchronized (buffer) {
				int amountRead = in.read(buffer);
				if (amountRead == -1) {
				    break;
				}
				out.write(buffer, 0, amountRead);
				notifyPartProcessed(nextFile);
				notifyProgressListeners(new ProgressStatus(i, pieces.length, nextFile.getPath()));
			    }
			}
			in.close();
			in = null;
		    } finally {
			if (in != null) {
			    in.close();
			    in = null;
			}
		    }
		}
		out.flush();
		out.close();
		out = null;
		notifyFileJoined(joinedFile);
	    }
	} catch (IOException ex) {
	    logger.log(Level.SEVERE, null, ex);
	    notifyError(new ProgressStatus(0, 0, ERROR_FILE_IO, true));
	} finally {
	    try {
		if (out != null) {
		    out.close();
		    out = null;
		}
	    } catch (IOException ex) {
		logger.log(Level.SEVERE, null, ex);
	    }
	}
    }
}
